晓晓的个人博客Logo
晓晓的个人博客
Chrome Extension的通信长连接与短连接
AI提炼icon
提炼
文章在《Chrome Extension 通信相关(汇总)》基础上,于讲解端到端通信流程前,阐述 Chrome Extension 中长、短连接的理论知识。消息通信是 Chrome Extension 核心,因各部分运行上下文不同,需设计多种通信方案。其通过发送方发消息、接收方设监听函数处理消息。通信通道建立方式有短连接与长连接。短连接为一次性通道,适用于发送单条消息并按需获取回复,虽能同步等待回复,但资源利用率低,且存在消息回复竞争问题。长连接则在有效时间段内可多次传递消息,依靠 runtime.Port 对象通信,该对象有特定的创建、不可用条件,可通过设置 runtime.Port.onDisconnect 监听函数知晓通道关闭状态,用于保障通信及唤起休眠的 Service Worker 等。
本文于 2025-07-22 14:14 首次发布,最后修改于 2025-08-24 16:17

写在开篇

继《Chrome Extension通信相关(汇总)》后,笔者在编写了部分端的通信 demo 之后,觉得有必要在说具体的端到端的通信流程之前,先给大家讲一点理论的东西:什么是长链接?什么是短连接?以及,它们在 Chrome Extension 中又是什么地位?

消息通信

消息通信 是 Chrome Extension 的一个核心概念。因为 Chrome Extension 自身就包括了 Content、Service Worker、option、popup 等部分,而且每个部分的执行上下文都不太一致,比如 Content 的执行上下文在插件的隔离环境中(与 Tab页 共享了 window与DOM的部分数据与函数 ),所以不仅为了一个插件内部的应用完整性,也为了插件更广的受众面,就需要设计“一个插件中的部分之间、插件与插件之间、插件与原生应用之间、插件与WEB端之间”的通信方案。

对于消息处理,Chrome Extension 的设计方案是:发送方发送消息、接收方设置接收消息的监听函数,在监听函数中根据参数内容有序处理对应消息。

而,关于通信通道建立与使用,插件有两种方式:

  • 一种是一次性消息通信通道,即,短连接,即时建立通信通道,发送完即销毁、关闭通信通道,一般用于:Chrome extension 部分之间发送单条消息,并且,按需获取接收方的回复的场景下;

  • 另一种是长连接,即,建立一个通信通道,在通道有效的时间段内,可以往复多次传递消息。当长链接通信通道建立时,每个端都会分配一个 runtime.Port 对象,以用于发送和接收消息;

短连接

对比长链接,短连接的优势在于:当发送方向接收方发送消息之后,可以同步等待接收方回复消息;

然而,从资源利用率上来说,每次都要建立通信通道,同等的发送消息数量下,相比长链接来说,这个执行效率和资源利用率肯定是低的,但又从现实的业务落地上来看,笔者还没有遇到过这种效率瓶颈,也就是一般的业务场景还没必要去考虑这个效率限制;

另外,针对短连接的消息回复,官方有这样一个提示:

If multiple pages are listening for onMessage events, only the first to call sendResponse() for a particular event will succeed in sending the response. All other responses to that event will be ignored.

关于这个提示,大致意思就是当短连接消息发送出去后,由于有多个监听函数在处理这条消息,而最终,发送方接收到的回复是多个监听函数中最先调用返回的那条消息,其他晚到的消息都会被忽略,这就是官方一直提醒的“消息回复竞争问题”,具体使用场景请参考笔者的另一篇文章《Chrome Extension短连接消息回复竞争问题》;

长连接

当在一个场景中,短时间内两个部分之间需要往复的发送好几个来回的消息时,一般建议使用长连接;

长连接中最重要的概念就是 runtime.Port 对象。当 Chrome Extension 的两个部分之间建立起通信通道之后,两个部分都会被分配一个 runtime.Port 对象,用于收发消息;其中,为了方便接下去的解说,我们将两个部分定义为:发起创建方 和 接收方

说到 runtime.Port 对象,它最重要的一块内容就是生命周期,即,什么时候创建?什么时候不可用?

那到底是什么时候创建 port 对象的呢?

答案:当 程序中调用 chrome.runtime.connect() 或 chrome.tabs.connect() 函数,且,有任意一个接收方创建了监听函数 时创建 port 对象,即,如果 发起创建方 单方面发起创建,但没有任何 接收方 的话,那就无法创建一条通信通道。

那又是什么时候 port 对象不可用,即,通信通道被关闭呢?以下罗列了几种情况:

  1. 当所有的接收方的监听函数都已经被销毁时,或,从一开始,根本就没有接收方设置任何监听函数时;

  2. 当 发起创建方 已经被卸载(比如 TAB页 被关闭或重定向、Service Worker 进入休眠状态);

  3. 任何一方调用 runtime.Port.disconnect() 函数时。

到此,为了保证通信可用性,你肯定有疑问:当通信通道关闭时,我们是否有方法知道呢?

当然是有的,就是我们在接收方或创建方的 port 上设置 runtime.Port.onDisconnect 监听函数后,当连接关闭时,相应的对方监听函数中就可以接收到断联的消息。

为什么是对方呢?经实践验证,如果是 发起创建方 主动调用了 disconnect 函数的,则对应的各个接收方会在 port.onDisconnect 的监听函数中在收到断联消息;当接收方主动调用 disconnect 函数的,则发起创建方会在 onDisconnect 的监听函数中在收到断联消息;

那监听这个通信通道关闭状态又有什么用呢?

  • 第一要务当然是为保证顺利通信做准备,比如发现这个通信通道被关闭了,那就再次发起创建通信通道以备后续收发消息;

  • 第二作用其实是“第一要务”的副作用,比如,当 Service Worker 进入休眠时会自动断开与 Content 层的长链接,而此时如果 Content 层监听发现了这个断联状态,那就让 Content 层重新向 Service Worker 请求建立通信通道,这时,Service Worker 会被从休眠状态中唤起。这个副作用可以说是“促使 Service Worker 保持长期激活状态”的一种技术方案。

写在最后

到此,理论层面的长短连接通信方式就讲完了,希望你可以将一些重点内容记忆一下,以便更好的理解后续“各个端与端之间的通信方式”~

9个赞
喜欢就点个赞吧